package com.people.core.log.aspect;

import ai.people.core.common.util.IpAddressUtil;
import com.alibaba.fastjson.JSONObject;
import com.people.core.log.config.LogRequestConfigProperties;
import com.people.core.log.entity.LogInfo;
import com.people.core.log.utils.LogInfoThreadLocal;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpRequest;
import org.springframework.util.StopWatch;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.List;

/**
 * 日志记录切面
 *
 * @author 李冠男
 * @date 2022/05/19
 */
//@Aspect
//@Component
public class LoggingAspect {

    /**
     * 线上打印日志阈值（超过200ms才会打印warn日志信息）
     */
    private static final long WARN_DURATION = 200L;

    /**
     * 省略长度阈值（线上如果单条信息量太大，则会进行省略展示）
     */
    private static final Integer OMIT_LENGTH = 200;
    /**
     * 换行
     */
    private static final String CRLF = "\r\n";

    @Autowired(required = false)
    LogRequestConfigProperties properties;


    @Bean
    @ConditionalOnProperty(prefix = "log", value = "enabled", havingValue = "true")
    @ConfigurationProperties(prefix = "log")
    public LogRequestConfigProperties properties() {
        return new LogRequestConfigProperties();
    }

    @Pointcut("execution(* *..controller..*(..)) && !@annotation(com.people.core.log.aspect.LoggingIgnore)")
    private void cut(){}

    @Around("cut()")
    public Object advice(ProceedingJoinPoint joinPoint) throws Throwable {
        if (properties == null || !properties.isEnabled()) {
            return joinPoint.proceed();
        }
        StringBuilder builder = new StringBuilder();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Class<?> targetClass = joinPoint.getTarget().getClass();
        org.slf4j.Logger logger = LoggerFactory.getLogger(targetClass);
        Method targetMethod = getMethod(joinPoint);
        Object object = null;

        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        builder.append(CRLF).append(StringUtils.repeat("=", 50)).append(CRLF);
        builder.append("请求url:").append(request.getRequestURL().toString()).append(CRLF);
        String requestMethod = request.getMethod();
        builder.append("请求http method:").append(requestMethod).append(CRLF);
        String ipAddress = IpAddressUtil.getIpAddress(request);
        builder.append("请求ip:").append(ipAddress).append(CRLF);
        builder.append("请求方法名称:").append(targetMethod.getDeclaringClass()).append(".").append(targetMethod.getName()).append(CRLF);

        LogInfo logInfo = new LogInfo();
        logInfo.setIpAddress(ipAddress);
        logInfo.setRequestUri(request.getRequestURL().toString());
        logInfo.setMethod(requestMethod);


//        Object[] args = filter(joinPoint.getArgs(), targetMethod);
//        String[] fieldNameList = getFieldNameList(joinPoint);
//        if (args.length > 0) {
//            int index = 0;
//            for (Object arg : args) {
//                if (arg instanceof BindingResult || arg instanceof ServletRequest || arg instanceof ServletResponse || arg instanceof HttpRequest || arg instanceof HttpResponse) {
//                    index++;
//                    continue;
//                }
//                builder.append("请求参数").append(fieldNameList[index]).append(StringPool.COLON).append(ConsoleUtil.shorterShowContent(arg, OMIT_LENGTH));
//                builder.append(CRLF);
//                index++;
//            }
//        }

        List<String> jsonStringParams = new ArrayList<>();

        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof BindingResult || arg instanceof HttpRequest || arg instanceof HttpResponse) {
                continue;
            }
            jsonStringParams.add(JSONObject.toJSONString(arg));
        }
        builder.append("请求参数:").append(jsonStringParams).append(CRLF);
        try {
            object = joinPoint.proceed();
        } catch (Exception e) {
            builder.append(CRLF);
            builder.append("ERROR!!执行方法时抛出异常");
            throw e;
        }finally {
            stopWatch.stop();
            long duration = stopWatch.getLastTaskTimeMillis();
            builder.append(String.format("===>请求耗时:%d毫秒.", duration));
            builder.append(CRLF).append(StringUtils.repeat("=", 50)).append(CRLF);

            if (duration > WARN_DURATION) {
                logger.warn(builder.toString());
            } else {
                logger.info(builder.toString());
            }
            logInfo.setJsonStringParams(JSONObject.toJSONString(args));
            LogInfoThreadLocal.set(logInfo);
        }
        return object;
    }

    private Method getMethod(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        return methodSignature.getMethod();
    }

    private Object[] filter(Object[] args, Method targetMethod) {
        Annotation[][] annotationList = targetMethod.getParameterAnnotations();
        Object[] values = new Object[args.length];
        for (int i = 0; i < annotationList.length; i++) {
            values[i] = args[i];
        }
        return values;
    }

    private String[] getFieldNameList(ProceedingJoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        return methodSignature.getParameterNames();
    }

}
